/**
 * Team members:
 * HengXing Liu 2022229044
 * YiYang Zhang 2022229047
 * ZeJun Liu 2022229092
 */
package project;

import java.io.*;
import java.util.*;
/**
 * A class to handle variable management
 */
public class VariableManage {
    private Map<String, Double> variables;
    private final String savingPath = "SavedData/Variables/";

    private boolean usedLast = false;

    public VariableManage(){
        variables = new HashMap<>();
        variables.put("last", 0.0);
    }
    /**
    * check a variable exists or not
     */
    public boolean isExist(String variableName)  {
        return variables.containsKey(variableName);
    }
    /**
     * add a variable
     * if it exists ,then modify it.
     */
    public void addOrModify(String variableName, double value) throws SyntaxErrorException {
        if(variableName.equals("last")){
            throw new SyntaxErrorException("last is not a variable");
        }
        variables.put(variableName, value);
    }
    /**
     * Delete the specified variable,
     * or deletes all existing variables.
     */
    public void remove(List<String> variableNames,boolean flgV) throws Exception {
        if(!flgV)return;
        if(variableNames.isEmpty()){
            if(!variables.isEmpty()){
                for(String variableName : variables.keySet()){
                    if(variableName.equals("last")) continue;
                    variableNames.add(variableName);
                }
            }
        }
        for(String str : variableNames) {
            if(!isExist(str)){
                throw new VariableException(str + " is not a variable");
            }
        }

        for(String str : variableNames){
            if(str.equals("last")){
                throw new SyntaxErrorException("last is not a variable");
            }
            variables.remove(str);
            System.out.println(str + " has been reset");
            if(LogManage.flag){
                LogManage.WriteFile(str + " has been reset\n");
            }
        }
    }
    /**
     * Gets the value of the specified variable.
     */
    public double getValue(String variableName) throws VariableException {
        if(!isExist(variableName)){
            throw new VariableException(variableName + " is not a variable");
        }
        if(variableName.equals("last") && !usedLast){
            throw new VariableException("last is null");
        }
        return variables.get(variableName);
    }
    /**
     * Get mappings for all variable-> variable values.
     */
    public Map<String, Double> getAllValue(){
        return variables;
    }
    /**
     * Update the special variable "last".
     */
    public void updLast(Double value){
        usedLast = true;
        variables.put("last", value);
    }
    /**
     * Saves the specified variable or all existing variables (including custom functions) to the specified file.
     */
    public void save(String fileName, List<String> savedVariables,FunctionManage fm) throws IOException {
        File file = new File(savingPath);
        if(!file.exists()){
            file.mkdirs();
        }

        file = new File(savingPath + fileName);
        if(!file.exists()){
            file.createNewFile();
        }

        FileWriter fileWriter = new FileWriter(savingPath + fileName);
        if(savedVariables != null && savedVariables.size() != 0){
            for(String variable: savedVariables){
                String command;
                if(fm.isContainFName(variable)){
                    String funcName = variable;
                    String tmpVariables = fm.getAllVariable().get(funcName).toString();
                    command = "define " + funcName + "(" + tmpVariables.substring(1,tmpVariables.length()-1)+") = " + fm.getExpression(funcName) + "\n";
                }else{
                    if(variable.equals("last")) continue;
                    command = "let " + variable + " = " + variables.get(variable) + "\n";
                }
                fileWriter.write(command);
            }
        }else{
            for(String variable : variables.keySet()){
                if(variable.equals("last")) continue;
                String command = "let " + variable + " = " + variables.get(variable) + "\n";
                fileWriter.write(command);
            }
            for(String funcName : fm.getAllName()){
                String tmpVariables = fm.getAllVariable().get(funcName).toString();
                String command = "define " + funcName + "(" + tmpVariables.substring(1,tmpVariables.length()-1)+") = " + fm.getExpression(funcName) + "\n";
                fileWriter.write(command);
            }
        }
        fileWriter.close();
    }
    /**
     * Print the file names of all variables that hold them.
     */
    public void listSavedDir() throws Exception {
        File file = new File(savingPath);
        String[] filesName = file.list();
        for(int i = 0; i < filesName.length; i++){
            System.out.println(filesName[i]);
            if(LogManage.flag){
                LogManage.WriteFile(filesName[i]+"\n");
            }
        }
    }
    /**
     * Loads all variables in the specified file (including custom functions).
     */
    public void load(String fileName, FunctionManage fm) throws FileNotFoundException, LexicalErrorException, TokenException, SyntaxErrorException {
        Scanner scanner = new Scanner(new File(savingPath + fileName));
        while(scanner.hasNextLine()){
            String command = scanner.nextLine();
            Tokenizer tokenizer = new Tokenizer(command);
            String letOrDefine = tokenizer.readNextToken().getIdentifier();//let
            if(letOrDefine.equals("let")){
                String variableName = tokenizer.readNextToken().getIdentifier();
                tokenizer.readNextToken();//等号
                Double value = tokenizer.readNextToken().getNumber();
                addOrModify(variableName, value);
            }else{//define
                ArrayList<String> tmpVariable = new ArrayList<>();
                //获取函数名
                String funcName = tokenizer.readNextToken().getIdentifier();
                tokenizer.readNextToken();//左括号
                while(tokenizer.hasNextToken()){
                    if(tokenizer.peekNextToken().toString().equals(")"))break;
                    Token token = tokenizer.readNextToken();
                    if(!token.isDelimiter()) {
                        tmpVariable.add(token.getIdentifier());
                    }
                }
                tokenizer.readNextToken();//右括号
                tokenizer.readNextToken();//等号
                fm.addOrModify(funcName, tmpVariable, tokenizer);
            }

        }
    }

}
